package com.ruiysoft.service.wechat.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ruiysoft.conf.RestTemplateConfig;
import com.ruiysoft.controller.wx.dto.OrderApplyRefundDto;
import com.ruiysoft.controller.wx.dto.OrderPayDto;
import com.ruiysoft.controller.wx.res.JsSignResponse;
import com.ruiysoft.controller.wx.res.TransferredResponse;
import com.ruiysoft.entity.sys.SysShopPayPlatform;
import com.ruiysoft.entity.sys.SysShopPersonnel;
import com.ruiysoft.entity.wechat.AccessToken;
import com.ruiysoft.entity.wechat.JsCodeSession;
import com.ruiysoft.entity.wechat.WXConstant;
import com.ruiysoft.exception.EBSysException;
import com.ruiysoft.service.sys.clent.LoginContextUtil;
import com.ruiysoft.service.wechat.IWeChatService;
import com.ruiysoft.service.wechat.redis.IRedisWeChatService;
import com.ruiysoft.util.WeChatUtil;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HTTP;
import org.apache.http.ssl.SSLContextBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.validation.Valid;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.math.BigDecimal;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.UUID;

/**
 * Created by 化增光 on 2018/10/22.
 */
@Service
public class WecChatServiceImpl implements IWeChatService {

    private static Logger logger = LoggerFactory.getLogger(WecChatServiceImpl.class);

    @Value("${wechat.loginMock}")
    boolean wechatLoginMock = false;

    @Resource
    IRedisWeChatService iRedisWeChatService;

    @Autowired
    @Qualifier(RestTemplateConfig.BEAN_REST_TEMPLATE_FOR_THIRD_PLATFORM)
    RestTemplate restTemplate;



//    @Override
//    public JSONObject getSessionKeyAndOpenId(String appId, String secret, String code) {
//        //临时写
//        String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+appId+
//                "&secret="+secret+"&js_code="+ code +"&grant_type=authorization_code";
//
//        System.out.println("url:"+url);
//        RestTemplate restTemplate = new RestTemplate();
//        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
//        System.out.println(JSON.toJSONString(responseEntity));
//        if(responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK){
//            String sessionData = responseEntity.getBody();
//            return JSONObject.parseObject(sessionData);
//        }
//        return null;
//    }

//    @Override
//    public String getAccessToken(SysShopPayPlatform payPlatform) {
//
//        String accessToken = iRedisWeChatService.getAccessToken(shopId);
//        if (StringUtils.isNotBlank(accessToken)) {
//            return accessToken;
//        } else {
//            //临时写
//            String remoteUrl = getAccessTokenURL(payPlatform.getAppId(),payPlatform.getSecret());
//            ResponseEntity<String> responseEntity = restTemplate.getForEntity(remoteUrl, String.class);
//            System.out.println("获取token返回-->"+JSON.toJSONString(responseEntity));
//            AccessToken newAt = JSON.parseObject(responseEntity.getBody(), AccessToken.class);
//
//            if (newAt != null && StringUtils.isNotBlank(newAt.getAccess_token())) {
//                iRedisWeChatService.saveAccessToken(shopId, newAt.getExpires_in() - 200,newAt.getAccess_token());
//                accessToken = newAt.getAccess_token();
//            } else {
//                throw new NullPointerException("Can't connect to WeiXin");
//            }
//
//            return accessToken;
//        }

//        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx9308044613fae7f8&secret=b70e875677ee6d6e97a7a4491b3739d0";
//        RestTemplate restTemplate = new RestTemplate();
//        ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
//        System.out.println(JSON.toJSONString(responseEntity));
//
//        return null;
  //  }

    @Override
    public JsCodeSession login(String code, String appId) {
        logger.info("wechatLoginMock login:{}",wechatLoginMock);
        if(wechatLoginMock){
            return loginMock(code,appId);
        }

        SysShopPayPlatform payPlatform = LoginContextUtil.instance().getShopPayPlatformByAppId(appId);
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid="+payPlatform.getAppId()
                +"&secret="+payPlatform.getSecret()+"&js_code="+ code +"&grant_type=authorization_code";
        logger.info("request to weixin url: {}",url);
        ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
        logger.info("response from weixin body: {}", JSON.toJSONString(responseEntity));
        if(responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK){
            String sessionData = responseEntity.getBody();
            JsCodeSession res = JSONObject.parseObject(sessionData,JsCodeSession.class);
            res.setAppId(payPlatform.getAppId());
            if (null == res || StringUtils.isBlank(res.getOpenid())){
                System.out.println("未获取到openid");
                return  null;
            }
            return res;
        }
        return null;
    }

    @Override
    public String getPrepayid(String appid, String paykey, String macid, String openid, @Valid OrderPayDto order) {
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        String trade_type = "JSAPI";
        String payAmountStr = String.valueOf(order.getPayAmount().multiply(new BigDecimal(100)).intValue());
        Map<String, String> map = new HashMap<String, String>();
        map.put("appid", appid);
        map.put("mch_id", macid);
        map.put("device_info", "WEB");
        map.put("nonce_str", nonceStr);
        map.put("body", order.getProductDesc());
        map.put("out_trade_no", order.getTransactionNo());
        map.put("total_fee", payAmountStr);
        if (StringUtils.isNotBlank(order.getAttach())){
            map.put("attach", order.getAttach());
        }
        map.put("trade_type", trade_type);
        map.put("notify_url", WXConstant.WX_PAY_NOTIFY_URL);
        System.out.println("微信支付回调:"+WXConstant.WX_PAY_NOTIFY_URL);
        map.put("openid", openid);
        String sign = WeChatUtil.sign(map, paykey);//参数加密
        map.put("sign", sign);
        String content = WeChatUtil.mapToXml(map);
        System.out.println("微信统一下单请求:"+content);
        String res = restTemplate.postForObject(WXConstant.WX_PLACE_AN_ORDER_URL, content, String.class);
        System.out.println("微信统一下单返回:-->"+res);
        Map<String, String> result = WeChatUtil.readStringXmlOut(res);
        if ("SUCCESS".equals(result.get("return_code"))) {
            if ("FAIL".equals(result.get("result_code"))) {
                System.out.println(result.get("err_code_des"));
                throw new EBSysException(result.get("err_code_des"));
            } else {
                return result.get("prepay_id");
            }
        } else {
            throw new EBSysException(result.get("return_msg"));
        }
    }

    @Override
    public OrderApplyRefundDto refund(SysShopPayPlatform payPlatform, int type, String orderNo, String refundNo, BigDecimal totalAmount, BigDecimal refundAmount)  {
        //SysShopPayPlatform payPlatform = LoginContextUtil.instance().getShopPayPlatform();
        String refundBody = transferRefundBody(payPlatform.getAppId(),payPlatform.getMchId(),payPlatform.getPayKey(),orderNo,refundNo,totalAmount,refundAmount);
        OrderApplyRefundDto dto = null;
        try {
            String res = transferRestTemplate(WXConstant.WX_REFUND_URL,refundBody,payPlatform.getMchId(),payPlatform.getCertificateUrl());
            System.out.println(res);
            Map<String, String> result =  WeChatUtil.readStringXmlOut(res);
            if ("SUCCESS".equals(result.get("return_code"))) {
                if("SUCCESS".equals(result.get("result_code"))){
                    dto = new OrderApplyRefundDto()
                            .setResultCode(result.get("return_code"))
                            .setTransactionNo(result.get("out_trade_no"))
                            .setRefundNo(result.get("out_refund_no"))
                            .setTransactionId(result.get("transaction_id"))
                            .setTransactionId(result.get("refund_id"))
                            .setRefundAmount(new BigDecimal(result.get("refund_fee")));
                }else{
                    dto = new OrderApplyRefundDto().setResultCode(result.get("return_code")).setErrorMessage(result.get("err_code_des"));
                }

            }else{
                dto = new OrderApplyRefundDto().setResultCode(result.get("return_code")).setErrorMessage(result.get("return_msg"));
            }

        } catch (KeyStoreException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (CertificateException e) {
            e.printStackTrace();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnrecoverableKeyException e) {
            e.printStackTrace();
        } catch (KeyManagementException e) {
            e.printStackTrace();
        }
        return dto;
    }

    @Override
    public String getAccessToken(String appId, String secret) {
        String accessToken = iRedisWeChatService.getAccessToken(appId);
        if (StringUtils.isNotBlank(accessToken)) {
            return accessToken;
        } else {
            //临时写
            String remoteUrl = getAccessTokenURL(appId,secret);
            ResponseEntity<String> responseEntity = restTemplate.getForEntity(remoteUrl, String.class);
            System.out.println("获取token返回-->"+JSON.toJSONString(responseEntity));
            AccessToken newAt = JSON.parseObject(responseEntity.getBody(), AccessToken.class);

            if (newAt != null && StringUtils.isNotBlank(newAt.getAccess_token())) {
                iRedisWeChatService.saveAccessToken(appId,newAt.getExpires_in() - 200,newAt.getAccess_token());
                accessToken = newAt.getAccess_token();
            } else {
                throw new NullPointerException("Can't connect to WeiXin");
            }

            return accessToken;
        }
    }

    @Override
    public byte[] wxacodeunlimit(String access_token, String scene, String url) {
        Map<String,Object> param = new HashMap<>();
        param.put("scene",scene);
        param.put("page",url);
        if(logger.isDebugEnabled()){
            logger.debug("wxacodeunlimit map param:{}",JSON.toJSONString(param));
        }
        MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
        headers.set(HTTP.CONTENT_TYPE, "application/json");
        HttpEntity formEntity = new HttpEntity(param,headers);

        return restTemplate.postForObject(WXConstant.QR_CODE_TICKET_URL+access_token,formEntity,byte[].class);
    }

    @Override
    public JsSignResponse jsSign(SysShopPersonnel personnel, SysShopPayPlatform payPlatform, @Valid OrderPayDto order) {
        Long timeStamp = System.currentTimeMillis() / 1000;
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        String prepayid = getPrepayid(payPlatform.getAppId(),payPlatform.getPayKey(),payPlatform.getMchId(),personnel.getOpenid(),order);
        Map<String, String> payparams = new HashMap<>();
        payparams.put("appId", payPlatform.getAppId());
        payparams.put("timeStamp", String.valueOf(timeStamp));
        payparams.put("nonceStr", nonceStr);
        payparams.put("package", "prepay_id=" + prepayid);
        payparams.put("signType", "MD5");
        String paySign = WeChatUtil.sign(payparams, payPlatform.getPayKey());

        JsSignResponse response = new JsSignResponse()
                .timeStamp(timeStamp)
                ._package("prepay_id=" + prepayid)
                .nonceStr(nonceStr)
                .signType("MD5")
                .paySign(paySign);
        return response;
    }

    @Override
    public TransferredResponse doTransfer(SysShopPayPlatform platform, String openid, String transactionNo, BigDecimal amount, String desc) {
        String content = transferDoTransferBody(platform.getAppId(),platform.getMchId(),platform.getPayKey(),openid,transactionNo,amount,desc);
//        String refundBody = transferRefundBody(payPlatform.getAppId(),payPlatform.getMchId(),payPlatform.getPayKey(),orderNo,refundNo,totalAmount,refundAmount);
//        OrderApplyRefundDto dto = null;
//        try {
//            String res = transferRestTemplate(WXConstant.WX_REFUND_URL,refundBody,payPlatform.getMchId(),payPlatform.getCertificateUrl());
//            System.out.println(res);
//        String res = null;
        try {
            System.out.println("请求报文--》"+content);
            String res = transferRestTemplate(WXConstant.WX_DO_TRANSFER_URL,content,platform.getMchId(),platform.getCertificateUrl());

            System.out.println("获取到返回值:"+res);
            Map<String, String> result =  WeChatUtil.readStringXmlOut(res);
            if ("SUCCESS".equals(result.get("return_code"))) {
                if ("SUCCESS".equals(result.get("result_code"))) {
                    return new TransferredResponse().setTransactionNo(result.get("partner_trade_no")).setPaymentNo(result.get("payment_no")).setPaymentTime(result.get("payment_time"));
                }
                throw new EBSysException(result.get("err_code_des"));
            }
            throw new EBSysException(result.get("return_msg"));
        } catch (Exception e) {
            e.printStackTrace();
            throw new EBSysException(e.getMessage());
        }
    }

    private String transferDoTransferBody(String appId, String mchId,String payKey, String openid, String transactionNo, BigDecimal amount, String desc) {
        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        Map<String, String> payparams = new LinkedHashMap<>();
        payparams.put("amount",amount.toString());
        payparams.put("check_name","NO_CHECK");
        payparams.put("desc", desc);
        payparams.put("mch_appid", appId);
        payparams.put("mchid", mchId);
//        payparams.put("device_info", "");
        payparams.put("nonce_str", nonceStr);
        payparams.put("openid",openid);
        payparams.put("partner_trade_no",transactionNo);
        payparams.put("spbill_create_ip", getLocalIP());
        String sign = WeChatUtil.sign(payparams, payKey);//参数加密
        payparams.put("sign", sign);
        String content = WeChatUtil.mapToXml2(payparams);
        return content;
    }


    public static String getLocalIP() {
        InetAddress addr = null;
        try {
            addr = InetAddress.getLocalHost();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }
        byte[] ipAddr = addr.getAddress();
        String ipAddrStr = "";
        for (int i = 0; i < ipAddr.length; i++) {
            if (i > 0) {
                ipAddrStr += ".";
            }
            ipAddrStr += ipAddr[i] & 0xFF;
        }
        return ipAddrStr;
    }


    public String transferRestTemplate(String url,String data,String mchId,String certificate_url) throws KeyStoreException, IOException, CertificateException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyManagementException, CertificateException {
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        FileInputStream inputStream = new FileInputStream(new File(certificate_url));
        keyStore.load(inputStream, mchId.toCharArray());
        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContextBuilder.create()
                .loadKeyMaterial(keyStore, mchId.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        HostnameVerifier hostnameVerifier = NoopHostnameVerifier.INSTANCE;
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext,  new String[]{"TLSv1"},
                null,hostnameVerifier);

        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();

        HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(httpclient);
        RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory);
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.parseMediaType("application/json; charset=UTF-8"));
//        requestHeaders.add("Content-Type", "application/json; charset=UTF-8");
//        requestHeaders.add("Host", "api.mch.weixin.qq.com");
//        requestHeaders.add("X-Requested-With", "XMLHttpRequest");
//        requestHeaders.add("Cache-Control", "max-age=0");
//        requestHeaders.add("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0) ");

        HttpEntity<String> requestEntity = new HttpEntity(data,requestHeaders);

        ResponseEntity<String> response = restTemplate.postForEntity(url, requestEntity, String.class);
        String res = response.getBody();
        return res;

    }

    private String transferRefundBody(String appId,String macId,String payKey,String orderNo, String refundNo, BigDecimal totalAmount, BigDecimal refundAmount) {

        String nonceStr = UUID.randomUUID().toString().replace("-", "");
        Map<String, String> payparams = new HashMap<String, String>();
        payparams.put("appid", appId);
        payparams.put("mch_id", macId);
        payparams.put("device_info", "WEB");
        payparams.put("out_trade_no",orderNo);
        payparams.put("out_refund_no",refundNo);
        payparams.put("total_fee",totalAmount.toString());
        payparams.put("refund_fee",totalAmount.toString());
        payparams.put("nonce_str", nonceStr);
        payparams.put("op_user_id", macId);
        payparams.put("notify_url", WXConstant.WX_REFUND_NOTIFY_URL);
        String sign = WeChatUtil.sign(payparams, payKey);//参数加密
        payparams.put("sign", sign);
        String content = WeChatUtil.mapToXml(payparams);
        return content;
    }

    private String getAccessTokenURL(String appid, String secret) {
        StringBuffer url = new StringBuffer();
        url.append(WXConstant.ACCESS_TOKEN_URL)
                .append("?")
                .append(WXConstant.GRANT_TYPE_CLIENT_CREDENTIAL)
                .append("&")
                .append("appid=" + appid)
                .append("&")
                .append("secret=" + secret);
        return url.toString();
    }


    private JsCodeSession loginMock(String code, String appId){
        JsCodeSession jsCodeSession = new JsCodeSession();
        jsCodeSession.setAppId(appId);
        jsCodeSession.setOpenid(code);
        return jsCodeSession;
    }



    public static void main(String[] args) {
//        RestTemplate restTemplate = new RestTemplate();
//        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
//        StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
//        stringHttpMessageConverter.setWriteAcceptCharset(true);
//
//        List<MediaType> mediaTypeList = new ArrayList<>();
//        mediaTypeList.add(MediaType.ALL);
//
//        for (int i = 0; i < restTemplate.getMessageConverters().size(); i++) {
//            if (restTemplate.getMessageConverters().get(i) instanceof StringHttpMessageConverter) {
//                restTemplate.getMessageConverters().remove(i);
//                restTemplate.getMessageConverters().add(i, stringHttpMessageConverter);
//            }
//            if(restTemplate.getMessageConverters().get(i) instanceof MappingJackson2HttpMessageConverter){
//                try{
//                    ((MappingJackson2HttpMessageConverter) restTemplate.getMessageConverters().get(i)).setSupportedMediaTypes(mediaTypeList);
//                }catch(Exception e){
//                    e.printStackTrace();
//                }
//            }
//        }
//
//        String content = "<xml><nonce_str>ee6a85068cc64dafa89b7de7cea50ff3</nonce_str><device_info>WEB</device_info><out_trade_no>CP20181111045938476</out_trade_no><openid>omDVs5XA0uDEv4LnkEW_R3TE_bz8</openid><appid>wx9308044613fae7f8</appid><total_fee>1</total_fee><sign><![CDATA[E3D8B2DC350F4674C39A07CF57F14BD5]]></sign><trade_type>JSAPI</trade_type><mch_id>1249480601</mch_id><body><![CDATA[des我的]]></body><notify_url><![CDATA[https://www.yogapro.cn/microshop/api/microprogram/payCallbackConfirm]]></notify_url></xml>";
////        HttpHeaders headers = new HttpHeaders();
////        MediaType type = MediaType.parseMediaType("application/json");
////        headers.setContentType(type);
////
////        headers.add("Accept", MediaType.APPLICATION_JSON.toString());
////        HttpEntity<String> formEntity = new HttpEntity<String>(content, headers);
//        String res = restTemplate.postForObject(WXConstant.WX_PLACE_AN_ORDER_URL, content, String.class);
//        System.out.println(res);
//        try {
//            String s = new String(res.getBytes(),"utf-8");
//            System.out.println(s);
//        } catch (UnsupportedEncodingException e) {
//            e.printStackTrace();
//        }
//
    }
}
